package com.stripe.android.link.ui.wallet

import androidx.compose.runtime.Immutable
import com.stripe.android.CardBrandFilter
import com.stripe.android.CardFundingFilter
import com.stripe.android.common.validation.isSupportedWithBillingConfig
import com.stripe.android.core.strings.ResolvableString
import com.stripe.android.core.strings.resolvableString
import com.stripe.android.link.LinkPaymentMethod
import com.stripe.android.link.ui.PrimaryButtonState
import com.stripe.android.model.ConsumerPaymentDetails
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.R
import com.stripe.android.uicore.forms.FormFieldEntry

@Immutable
internal data class WalletUiState(
    val paymentDetailsList: List<ConsumerPaymentDetails.PaymentDetails>,
    val email: String,
    val allowLogOut: Boolean,
    val cardBrandFilter: CardBrandFilter,
    val cardFundingFilter: CardFundingFilter,
    val selectedItemId: String?,
    val isProcessing: Boolean,
    val isSettingUp: Boolean,
    val merchantName: String,
    val sellerBusinessName: String?,
    val primaryButtonLabel: ResolvableString,
    val secondaryButtonLabel: ResolvableString?,
    val hasCompleted: Boolean,
    val addPaymentMethodOptions: List<AddPaymentMethodOption>,
    val collectMissingBillingDetailsForExistingPaymentMethods: Boolean,
    val userSetIsExpanded: Boolean? = null,
    val cardBeingUpdated: String? = null,
    val errorMessage: ResolvableString? = null,
    val expiryDateInput: FormFieldEntry = FormFieldEntry(null),
    val cvcInput: FormFieldEntry = FormFieldEntry(null),
    val addBankAccountState: AddBankAccountState = AddBankAccountState.Idle,
    val alertMessage: ResolvableString? = null,
    val paymentSelectionHint: ResolvableString? = null,
    val isAutoSelecting: Boolean = false,
    val hasAttemptedAutoSelection: Boolean = false,
    val signupToggleEnabled: Boolean,
    val billingDetailsCollectionConfiguration: PaymentSheet.BillingDetailsCollectionConfiguration,
    val isValidating: Boolean = false,
) {

    val selectedItem: ConsumerPaymentDetails.PaymentDetails?
        get() = if (selectedItemId != null) {
            paymentDetailsList.firstOrNull { it.id == selectedItemId }
        } else {
            paymentDetailsList.firstOrNull()
        }

    val selectedCard: ConsumerPaymentDetails.Card?
        get() = selectedItem as? ConsumerPaymentDetails.Card

    val mandate: ResolvableString?
        get() = selectedItem?.makeMandateText(
            isSettingUp = isSettingUp,
            merchantName = merchantName,
            sellerBusinessName = sellerBusinessName,
            signupToggleEnabled = signupToggleEnabled
        )

    val shouldShowLoadingState: Boolean
        get() = paymentDetailsList.isEmpty() || isAutoSelecting

    val isExpanded: Boolean
        get() = userSetIsExpanded ?: (selectedItem?.let { isItemAvailable(it) } != true)

    val primaryButtonState: PrimaryButtonState
        get() {
            val card = selectedItem as? ConsumerPaymentDetails.Card
            val isExpired = card?.isExpired == true
            val requiresCvcRecollection = card?.cvcCheck?.requiresRecollection ?: false

            val isMissingExpiryDateInput = (expiryDateInput.isComplete && cvcInput.isComplete).not()
            val isMissingCvcInput = cvcInput.isComplete.not()

            val disableButton = (isExpired && isMissingExpiryDateInput) ||
                (requiresCvcRecollection && isMissingCvcInput) || (cardBeingUpdated != null) ||
                selectedItem?.let { isItemAvailable(it) } != true ||
                addBankAccountState is AddBankAccountState.Processing

            return when {
                hasCompleted -> {
                    PrimaryButtonState.Completed
                }
                isProcessing -> {
                    PrimaryButtonState.Processing
                }
                disableButton -> {
                    PrimaryButtonState.Disabled
                }
                else -> {
                    PrimaryButtonState.Enabled
                }
            }
        }

    val addBankAccountOption: AddPaymentMethodOption.Bank?
        get() = addPaymentMethodOptions.firstNotNullOfOrNull { it as? AddPaymentMethodOption.Bank }

    val canAddNewPaymentMethod: Boolean
        get() = addPaymentMethodOptions.isNotEmpty()

    fun isItemAvailable(item: ConsumerPaymentDetails.PaymentDetails): Boolean {
        if (item.isSupportedWithBillingConfig(billingDetailsCollectionConfiguration).not()) {
            return false
        }
        if (item !is ConsumerPaymentDetails.Card) {
            return true
        }
        return cardBrandFilter.isAccepted(item.brand) && cardFundingAccepted(item)
    }

    private fun cardFundingAccepted(item: ConsumerPaymentDetails.PaymentDetails): Boolean {
        return when (item) {
            is ConsumerPaymentDetails.BankAccount -> true
            is ConsumerPaymentDetails.Card -> {
                cardFundingFilter.isAccepted(item.funding.cardFunding)
            }
            is ConsumerPaymentDetails.Passthrough -> true
        }
    }

    fun updateWithResponse(
        response: List<LinkPaymentMethod.ConsumerPaymentDetails>,
    ): WalletUiState {
        val paymentDetails = response.map { it.details }

        val selectedItem = if (selectedItemId != null) {
            paymentDetails.firstOrNull { it.id == selectedItemId }
        } else {
            paymentDetails.firstOrNull { it.isDefault } ?: paymentDetails.firstOrNull()
        }

        val expanded = (userSetIsExpanded == true) || (selectedItem?.let { !isItemAvailable(it) } == true)

        return copy(
            paymentDetailsList = paymentDetails,
            isProcessing = false,
            userSetIsExpanded = expanded,
            cardBeingUpdated = null
        )
    }
}

private fun ConsumerPaymentDetails.PaymentDetails.makeMandateText(
    isSettingUp: Boolean,
    merchantName: String,
    sellerBusinessName: String?,
    signupToggleEnabled: Boolean
): ResolvableString? {
    return when (this) {
        is ConsumerPaymentDetails.BankAccount -> when {
            signupToggleEnabled && sellerBusinessName != null -> resolvableString(
                R.string.stripe_wallet_bank_account_terms_merchant_and_seller,
                merchantName,
                sellerBusinessName,
                merchantName
            )
            else -> resolvableString(R.string.stripe_wallet_bank_account_terms)
        }
        is ConsumerPaymentDetails.Card,
        is ConsumerPaymentDetails.Passthrough -> when {
            signupToggleEnabled -> resolvableString(
                id = R.string.stripe_paymentsheet_card_mandate_signup_toggle_off,
                merchantName
            )
            isSettingUp -> resolvableString(
                id = R.string.stripe_paymentsheet_card_mandate,
                merchantName
            )
            else -> null
        }
    }
}
